﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Common.Utilities;

namespace Workflows.Components.Entities.Distances.Comparers
{
    /// <summary>
    /// 
    /// </summary>
    public class PostalCodeComparer:IAttributeComparer
    {
        private string _Zip5Pattern = @"^(\d\d\d\d\d)$";
        private string _Zip9Pattern = @"^(\d\d\d\d\d)\s?-\s?\d\d\d\d$";
        private string _Zip9Pattern2 = @"^(\d\d\d\d\d)\s?-?\s?\d\d\d\d$";
        private string _UKPattern = @"[A-Z]{1,2}\d[A-Z\d]? \d[ABD-HJLNP-UW-Z]{2}";
        private string _CanadaPattern = @"[ABCEGHJKLMNPRSTVXY]\d[A-Z] \d[A-Z]\d";
        private string _GermanPattern = @"^([0124678][0-9]{4})$";
        private string _ChinaPattern = @"\d{6}";

        #region Implementation of IAttributeComparer

        /// <summary>
        /// specify either use code snippet or class
        /// </summary>
        public AttributeComparerType ComparerType
        {
            get
            {
                return AttributeComparerType.ClassComparer;
            }
        }

        private SqlDbType _FieldDbType = SqlDbType.VarChar;
        /// <summary>
        /// 
        /// </summary>
        public SqlDbType FieldDbType
        {
            get
            {
                return _FieldDbType;
            }
            set
            {
                _FieldDbType = value;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value1"></param>
        /// <param name="value2"></param>
        /// <param name="penalty"></param>
        /// <param name="conn"></param>
        /// <param name="unevenDistTableName"></param>
        /// <returns></returns>
        public double Compare(object value1, object value2, double penalty, SqlConnection conn, string unevenDistTableName)
        {
            if (value1 == null || value2 == null)
                return 0;

            string strVal1 = value1.ToString();
            string strVal2 = value2.ToString();
            if (strVal1.Length == 0 || strVal2.Length == 0)
                return 0;

            try
            {
                if (strVal1.ToLower() == strVal2.ToLower())
                {
                    return 1.00;
                }
                else
                {
                    // US
                    string us1 = string.Empty;
                    string us2 = string.Empty;
                    if (Regex.IsMatch(strVal1, this._Zip5Pattern))
                        us1 = Regex.Match(strVal1, this._Zip5Pattern).Groups[1].Value;
                    else if (Regex.IsMatch(strVal1, this._Zip9Pattern))
                        us1 = Regex.Match(strVal1, this._Zip9Pattern).Groups[1].Value;
                    else if (Regex.IsMatch(strVal1, this._Zip9Pattern2))
                        us1 = Regex.Match(strVal1, this._Zip9Pattern2).Groups[1].Value;
                    if (Regex.IsMatch(strVal2, this._Zip5Pattern))
                        us2 = Regex.Match(strVal2, this._Zip5Pattern).Groups[1].Value;
                    else if (Regex.IsMatch(strVal2, this._Zip9Pattern))
                        us2 = Regex.Match(strVal2, this._Zip9Pattern).Groups[1].Value;
                    else if (Regex.IsMatch(strVal2, this._Zip9Pattern2))
                        us2 = Regex.Match(strVal2, this._Zip9Pattern2).Groups[1].Value;
                    if (us1 == us2 && us2.Length > 0 && us1.Length > 0)
                    {
                        return 1.00;
                    }
                    // UK
                    string uk1 = string.Empty;
                    string uk2 = string.Empty;
                    if (Regex.IsMatch(strVal1, this._UKPattern))
                        uk1 = Regex.Match(strVal1, this._UKPattern).Value;
                    if (Regex.IsMatch(strVal2, this._UKPattern))
                        uk2 = Regex.Match(strVal2, this._UKPattern).Value;
                    if (uk1.Length > 0 && uk2.Length > 0 && uk1 == uk2)
                    {
                        return 1.0;
                    }
                    // canada
                    string ca1 = string.Empty;
                    string ca2 = string.Empty;
                    if (Regex.IsMatch(strVal1, this._CanadaPattern))
                        ca1 = Regex.Match(strVal1, this._CanadaPattern).Value;
                    if (Regex.IsMatch(strVal2, this._CanadaPattern))
                        ca2 = Regex.Match(strVal2, this._CanadaPattern).Value;
                    if (ca1.Length > 0 && ca2.Length > 0 && ca1 == ca2)
                    {
                        return 1.0;
                    }
                    // german
                    string de1 = string.Empty;
                    string de2 = string.Empty;
                    if (Regex.IsMatch(strVal1, this._GermanPattern))
                        de1 = Regex.Match(strVal1, this._GermanPattern).Value;
                    if (Regex.IsMatch(strVal2, this._GermanPattern))
                        de2 = Regex.Match(strVal2, this._GermanPattern).Value;
                    if (de1.Length > 0 && de2.Length > 0 && de1 == de2)
                    {
                        return 1.0;
                    }
                    // china
                    string cn1 = string.Empty;
                    string cn2 = string.Empty;
                    if (Regex.IsMatch(strVal1, this._ChinaPattern))
                        cn1 = Regex.Match(strVal1, this._ChinaPattern).Value;
                    if (Regex.IsMatch(strVal2, this._ChinaPattern))
                        cn2 = Regex.Match(strVal2, this._ChinaPattern).Value;
                    if (cn1.Length > 0 && cn2.Length > 0 && cn1 == cn2)
                    {
                        return 1.0;
                    }
                }

                return penalty;
            }
            catch (Exception ex)
            {
                ExceptionHandler.HandlesException(ex);
                return 0.0;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="values"></param>
        /// <param name="conn"></param>
        /// <param name="unevenDistTableName"></param>
        /// <returns></returns>
        public object Merge(List<object> values, SqlConnection conn, string unevenDistTableName)
        {
            if (values == null || values.Count == 0)
                return null;
            Dictionary<string, int> valueByCount = new Dictionary<string, int>();
            foreach (object obj in values)
            {
                string val = obj.ToString().ToUpper();
                if (!string.IsNullOrEmpty(val))
                {
                    if (valueByCount.ContainsKey(val))
                    {
                        valueByCount[val] = valueByCount[val] + 1;
                    }
                    else
                    {
                        valueByCount.Add(val, 1);
                    }
                }
            }
            if (valueByCount.Count == 0)
                return null;
            else if (valueByCount.Count == 1)
            {
                string[] vals = new string[valueByCount.Count];
                valueByCount.Keys.CopyTo(vals, 0);
                return vals[0];
            }
            else
            {
                int maxFreq = 0;
                object mode = null;
                foreach(string val in valueByCount.Keys)
                {
                    if(valueByCount[val]>maxFreq)
                    {
                        maxFreq = valueByCount[val];
                        mode = val;
                    }
                }
                return mode;
            }
        }

        #endregion
    }
}
